#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include "led_desc.h"

#define LED_OFF _IOW('l',0,int)
#define LED_ON _IOW('l',1,int)
#define LED_READ _IOWR('l',2,int)


MODULE_LICENSE("GPL");

static dev_t ledDevnum;        // 设备号
static struct cdev* ledCdev;
static struct class* ledCls;
static int ledCount;
static led_desc_t* ledDevs;

long led_ioctl(struct file* filePtr, unsigned int request, unsigned long arg)
{
    int ledIndex, readLedIndex;
    copy_from_user(&ledIndex, (void*)arg, _IOC_SIZE(request));

    switch(request)
    {
        case LED_ON:
        {
            gpio_set_value(ledDevs[ledIndex - 1].m_gpioNumber, 1);
            break;
        }
        case LED_OFF:
        {
            gpio_set_value(ledDevs[ledIndex - 1].m_gpioNumber, 0);
            break;
        }
        case LED_READ:
        {
            readLedIndex = gpio_get_value(ledDevs[ledIndex - 1].m_gpioNumber);
            copy_to_user((void*)arg, &readLedIndex, _IOC_SIZE(request));
            break;
        }
        default:printk("request is invalid %s is failed\n", __func__); return -EINVAL;
    }
    return 0;
}

struct file_operations ledFops = 
{
    .owner = THIS_MODULE,
    .unlocked_ioctl = led_ioctl
};

int led_drv_probe(struct platform_device* pdev)
{
    int i;
    ledCount = ((leds_info_t*)(pdev->dev.platform_data))->m_ledCounter;
    ledDevs = ((leds_info_t*)(pdev->dev.platform_data))->m_leds;

    alloc_chrdev_region(&ledDevnum, 50, 1, "leds");
    ledCdev = cdev_alloc();
    cdev_init(ledCdev, &ledFops);
    cdev_add(ledCdev, ledDevnum, 1);

    ledCls = class_create(THIS_MODULE, "LEDS");
    device_create(ledCls, NULL, ledDevnum, NULL, "leds");

    for(i = 0; i < ledCount; i++)
    {
        gpio_request(ledDevs[i].m_gpioNumber, ledDevs[i].m_name);
        gpio_direction_output(ledDevs[i].m_gpioNumber, 0);
    }

    printk("%s is execute\n", __func__);
    return 0;
}

int led_drv_remove(struct platform_device* pdev)
{
    int i;
    for(i = 0; i < ledCount; i++)
    {
        gpio_free(ledDevs[i].m_gpioNumber);
    }

    device_destroy(ledCls, ledDevnum);
    class_destroy(ledCls);

    cdev_del(ledCdev);
    unregister_chrdev_region(ledDevnum, 1);
    
    printk("%s is execute\n", __func__);
    return 0;
}

struct platform_driver led_drv = 
{
    .driver = 
    {
        .name = "myleds"
    },
    .probe = led_drv_probe,
    .remove = led_drv_remove
};

static int __init led_drv_init(void)
{
    platform_driver_register(&led_drv);
    return 0;
}

static void __exit led_drv_exit(void)
{
    platform_driver_unregister(&led_drv);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
